[アップデート]テンプレートの再利用が簡単に!!CloudFormationでモジュールが利用可能になりました
CX事業本部@大阪の岩田です。少し間が空いてしまいましたが、11/24付のアップデートによりCloudFormationでモジュールが利用可能になりました。このブログではモジュール機能について簡単に紹介させて頂きます。
概要
モジュールはCloudFormationのテンプレートを再利用するための機能です。CloudFormationのテンプレートをモジュールとしてCloudFormationレジストリに登録しておくことで、別のテンプレートから参照して取り込むことが可能です。
例えば
- EC2インスタンス
- セキュリティグループ
を作成するテンプレートAがあるとします。このテンプレートAをモジュールとして登録しておき、テンプレートBから参照することで、テンプレートBにもEC2インスタンスとセキュリティグループの定義が展開されます。テンプレートB側ではモジュールを参照するだけで、EC2インスタンスとセキュリティグループがデプロイできます。このようにモジュールを利用するとリソース構成をモジュール内にカプセル化できるので、モジュール利用側はリソースの詳細なプロパティやベストプラクティスについて理解する必要はありません。そのため
- ネットワークの専門家がVPC等のリソースを構築するモジュールを作成しておく
- 開発者は登録済みのモジュールを利用してネットワーク関連のリソースを作成する
といった運用が可能です。
また、モジュール利用側のテンプレートとモジュールの間では
Fn::Ref
やFn::Sub
を利用してリソースの情報を参照する(※利用側のテンプレートからモジュールを参照することも、その逆も両方可能)- 利用側のテンプレートからモジュール側のテンプレートにParametersを渡す
といったことも可能で、モジュールを利用するテンプレートを柔軟に構築できます。
公式ドキュメントではモジュールのメリットとして
- 予測可能性
- 再利用性
- トレーサビリティ
- 管理性
といったメリットが紹介されています。
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/modules.html
やってみる
それでは早速モジュールを試してみましょう。以下AWSブログの手順に沿って進めていきます。
Introducing AWS CloudFormation modules
モジュールの作成と登録にはCloudFormation CLIを利用するので、pip等を利用して事前にインストールしておきましょう。私の環境では以下のコマンドを利用しました。
$pip install cloudformation-cli-python-plugin
モジュールの作成とCloudFormationレジストリへの登録
まずCloudFormation CLIを利用してプロジェクトの雛形を作成します、
$cfn init
コマンドを実行するとTUIでいくつかの入力が要求されます。まず開発対象(リソース/モジュール)の選択が要求されるので、モジュールを意味するm
を入力します。続いてモジュール名を聞かれるので、適当なモジュール名を入力します。今回はClassmethod::S3::Bucket::MODULE
というモジュール名にしました。モジュール名は::MODULE
で終わる必要があるので、ここは注意が必要です。
Initializing new project Do you want to develop a new resource(r) or a module(m)?. >> m What's the name of your module type? (<Organization>::<Service>::<Name>::MODULE) >> Classmethod::S3::Bucket::MODULE Directory /Users/xxx/yyy/zzz/fragments Created Initialized a new project in /Users/xxx/yyy/zzz/
初期化が完了すると、以下のような構造でファイルとディレクトリが作成されます。
. ├── .rpdk-config ├── fragments │ └── sample.json └── rpdk.log
- fragmentsディレクトリはモジュールとして利用するためのCloudFormationテンプレートを配置するディレクトリです。初期化したさいにサンプルのsample.jsonというファイルが生成されています。
- .rpdk-configというファイルはモジュール名前などの情報を保持するファイルです。
- rpdk.logというファイルはCloudFormation CLIの出力が記録されるログ・ファイルです。
fragmentsディレクトリ配下のsample.jsonをs3.jsonにリネームし、以下の内容に置き換えます。※現状モジュールとして利用するCloudFormationテンプレートはJSON形式のみ対応しています。YAMLには対応していません。
{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "Create a S3 bucket that follows MyCompany's standards", "Parameters": { "KMSKeyAlias": { "Description": "The alias for your KMS key. If you will leave this field empty KMS key alias won't be created along the key.", "Type": "String", "AllowedPattern": "^(?!aws)([a-zA-Z0-9\\-\\_\\/]){1,32}$|^$", "MinLength": 0, "MaxLength": 32, "ConstraintDescription": "KMS key alias must be at least 1 and no more than 32 characters long, can contain lowercase and uppercase letters, numbers, hyphens, underscores and forward slashes and cannot begin with aws." }, "ReadOnlyArn": { "Description": "Provide ARN of an existing Principal (role) that will be granted with read only access to the S3 bucket (e.g. 'arn:aws:iam::123456789xxx:role/myS3ROrole'). If not specified, access will be granted to current AWS account:root only. CF deployment will fail and rollback for non-existing ARN.", "Type": "String", "Default": "", "AllowedPattern": "^(arn:aws:iam::\\d{12}:role(\\/|\\/[\\w\\!\\\"\\#\\$\\%\\'\\(\\)\\*\\+\\,\\-\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\\\\\]\\^\\`\\{\\|\\}\\~]{1,510}\\/)[\\w\\+\\=\\,\\.\\@\\-]{1,64})$|^$", "ConstraintDescription": "IAM role ARN must start with arn:aws:iam::<12-digit AWS account number>:role/[<path>/]<name of role>. The name of role must be at least 1 and no more than 64 characters long, can contain lowercase letters, uppercase letters, numbers, plus (+), equal (=), comma (,), period (.), at (@), underscore (_), and hyphen (-). Path is optional and must not exceed 510 characters." }, "ReadWriteArn": { "Description": "Provide ARN of an existing Principal (role) that will be granted with read and write access to the S3 bucket (e.g. 'arn:aws:iam::123456789xxx:role/myS3RWrole'). If not specified, access will be granted to current AWS account:root only. CF deployment will fail and rollback for non-existing ARN.", "Type": "String", "Default": "", "AllowedPattern": "^(arn:aws:iam::\\d{12}:role(\\/|\\/[\\w\\!\\\"\\#\\$\\%\\'\\(\\)\\*\\+\\,\\-\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\\\\\]\\^\\`\\{\\|\\}\\~]{1,510}\\/)[\\w\\+\\=\\,\\.\\@\\-]{1,64})$|^$", "ConstraintDescription": "IAM role ARN must start with arn:aws:iam::<12-digit AWS account number>:role/[<path>/]<name of role>. The name of role must be at least 1 and no more than 64 characters long, can contain lowercase letters, uppercase letters, numbers, plus (+), equal (=), comma (,), period (.), at (@), underscore (_), and hyphen (-). Path is optional and must not exceed 510 characters." } }, "Resources": { "KmsKey": { "Type": "AWS::KMS::Key", "DeletionPolicy": "Retain", "Properties": { "Enabled": true, "EnableKeyRotation": true, "KeyPolicy": { "Version": "2012-10-17", "Statement": [ { "Sid": "Give AWS account:root full control over the KMS key", "Effect": "Allow", "Principal": { "AWS": { "Fn::Sub": "arn:${AWS::Partition}:iam::${AWS::AccountId}:root" } }, "Action": [ "kms:*" ], "Resource": "*" }, { "Sid": "Give ReadOnlyRole access to use KMS key for decryption", "Effect": "Allow", "Principal": { "AWS": { "Ref": "ReadOnlyArn" } }, "Action": [ "kms:Decrypt", "kms:DescribeKey" ], "Resource": "*" }, { "Sid": "Give the ReadWriteRole access to use KMS key for encryption and decryption", "Effect": "Allow", "Principal": { "AWS": { "Ref": "ReadWriteArn" } }, "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt", "kms:GenerateDataKey*", "kms:DescribeKey" ], "Resource": "*" } ] } } }, "KmsKeyAlias": { "Type": "AWS::KMS::Alias", "Properties": { "AliasName": { "Fn::Sub": "alias/${KMSKeyAlias}" }, "TargetKeyId": { "Ref": "KmsKey" } } }, "Bucket": { "Type": "AWS::S3::Bucket", "Properties": { "AccessControl": "BucketOwnerFullControl", "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { "ServerSideEncryptionByDefault": { "KMSMasterKeyID": { "Ref": "KmsKey" }, "SSEAlgorithm": "aws:kms" } } ] }, "BucketName": { "Fn::Sub": "${AWS::StackName}-${AWS::AccountId}-${AWS::Region}" }, "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "IgnorePublicAcls": true, "BlockPublicPolicy": true, "RestrictPublicBuckets": true } } }, "BucketPolicy": { "Type": "AWS::S3::BucketPolicy", "Properties": { "Bucket": { "Ref": "Bucket" }, "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Sid": "DenyIncorrectEncryptionHeader", "Effect": "Deny", "Principal": "*", "Action": "s3:PutObject", "Resource": { "Fn::Sub": "arn:${AWS::Partition}:s3:::${Bucket}/*" }, "Condition": { "StringEquals": { "s3:x-amz-server-side-encryption": "AES256" } } }, { "Sid": "DenyPublicReadACL", "Effect": "Deny", "Principal": "*", "Action": [ "s3:PutObject", "s3:PutObjectAcl" ], "Resource": { "Fn::Sub": "arn:${AWS::Partition}:s3:::${Bucket}/*" }, "Condition": { "StringEquals": { "s3:x-amz-acl": [ "public-read", "public-read-write", "authenticated-read" ] } } }, { "Sid": "DenyPublicReadGrant", "Effect": "Deny", "Principal": "*", "Action": [ "s3:PutObject", "s3:PutObjectAcl" ], "Resource": { "Fn::Sub": "arn:${AWS::Partition}:s3:::${Bucket}/*" }, "Condition": { "StringLike": { "s3:x-amz-grant-read": [ "*http://acs.amazonaws.com/groups/global/AllUsers*", "*http://acs.amazonaws.com/groups/global/AuthenticatedUsers*" ] } } }, { "Sid": "DenyNonHttpsConnections", "Effect": "Deny", "Principal": "*", "Action": [ "s3:PutObject", "s3:GetObject" ], "Resource": { "Fn::Sub": "arn:${AWS::Partition}:s3:::${Bucket}/*" }, "Condition": { "Bool": { "aws:SecureTransport": false } } }, { "Sid": "Give ReadOnlyRole access to get objects from bucket and list bucket", "Effect": "Allow", "Principal": { "AWS": { "Ref": "ReadOnlyArn" } }, "Action": [ "s3:GetObject", "s3:GetObjectTagging", "s3:ListBucket", "s3:ListBucketByTags" ], "Resource": [ { "Fn::Sub": "arn:${AWS::Partition}:s3:::${Bucket}" }, { "Fn::Sub": "arn:${AWS::Partition}:s3:::${Bucket}/*" } ] }, { "Sid": "Give the ReadWriteRole access to get and put objects from and to bucket and list bucket and multipart uploads", "Effect": "Allow", "Principal": { "AWS": { "Ref": "ReadWriteArn" } }, "Action": [ "s3:DeleteObject", "s3:DeleteObjectTagging", "s3:GetObject", "s3:GetObjectTagging", "s3:ListBucket", "s3:ListBucketByTags", "s3:PutObject", "s3:PutObjectTagging" ], "Resource": [ { "Fn::Sub": "arn:${AWS::Partition}:s3:::${Bucket}" }, { "Fn::Sub": "arn:${AWS::Partition}:s3:::${Bucket}/*" } ] } ] } } } }, "Outputs": { "BucketArn": { "Description": "ARN of the bucket created.", "Value": { "Fn::GetAtt": [ "Bucket", "Arn" ] } }, "BucketName": { "Description": "Name of the bucket created.", "Value": { "Ref": "Bucket" } }, "KmsKeyAlias": { "Description": "Alias of SSE-KMS Customer Managed Key used to encrypt S3 bucket content.", "Value": { "Ref": "KmsKeyAlias" } }, "KmsKeyArn": { "Description": "ARN of SSE-KMS Customer Managed Key used to encrypt S3 bucket content.", "Value": { "Fn::GetAtt": [ "KmsKey", "Arn" ] } } } }
モジュール化するテンプレートの準備ができたので、以下のコマンドでモジュールをCloudFormationレジストリに登録します。
$cfn submit
しばらく待つとCloudFormation レジストリへの登録が完了し、以下の用に出力されます。
Successfully submitted type. Waiting for registration with token '7e150599-078e-49c2-9214-3e0e3fdfa00a' to complete. Registration complete. {'ProgressStatus': 'COMPLETE', 'Description': 'Deployment is currently in DEPLOY_STAGE of status COMPLETED; ', 'TypeArn': 'arn:aws:cloudformation:ap-northeast-1:<AWSアカウントID>:type/module/Classmethod-S3-Bucket-MODULE', 'TypeVersionArn': 'arn:aws:cloudformation:ap-northeast-1:<AWSアカウントID>:type/module/Classmethod-S3-Bucket-MODULE/00000001', 'ResponseMetadata': {'RequestId': 'be6aad46-28a1-4579-a08f-1e296f3779ea', 'HTTPStatusCode': 200, 'HTTPHeaders': {'x-amzn-requestid': 'be6aad46-28a1-4579-a08f-1e296f3779ea', 'content-type': 'text/xml', 'content-length': '701', 'date': 'Sat, 28 Nov 2020 05:28:59 GMT'}, 'RetryAttempts': 0}}
これで登録完了です。
CloudFormationレジストリの登録内容をAWS CLIから確認してみましょう
$aws cloudformation describe-type --type MODULE --type-name Classmethod::S3::Bucket::MODULE
以下のようなレスポンスが返却されます。
{ "Arn": "arn:aws:cloudformation:ap-northeast-1:944137583148:type/module/Classmethod-S3-Bucket-MODULE/00000001", "Type": "MODULE", "TypeName": "Classmethod::S3::Bucket::MODULE", "DefaultVersionId": "00000001", "IsDefaultVersion": true, "Description": "Schema for Module Fragment of type Classmethod::S3::Bucket::MODULE", "Schema": "{\n \"typeName\": \"Classmethod::S3::Bucket::MODULE\",\n \"description\": \"Schema for Module Fragment of type Classmethod::S3::Bucket::MODULE\",\n \"properties\": {\n \"Parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"KMSKeyAlias\": {\n \"type\": \"object\",\n \"properties\": {\n \"Type\": {\n \"type\": \"string\"\n },\n \"Description\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"Type\",\n \"Description\"\n ],\n \"description\": \"The alias for your KMS key. If you will leave this field empty KMS key alias won't be created along the key.\"\n },\n \"ReadOnlyArn\": {\n \"type\": \"object\",\n \"properties\": {\n \"Type\": {\n \"type\": \"string\"\n },\n \"Description\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"Type\",\n \"Description\"\n ],\n \"description\": \"Provide ARN of an existing Principal (role) that will be granted with read only access to the S3 bucket (e.g. 'arn:aws:iam::123456789xxx:role/myS3ROrole'). If not specified, access will be granted to current AWS account:root only. CF deployment will fail and rollback for non-existing ARN.\"\n },\n \"ReadWriteArn\": {\n \"type\": \"object\",\n \"properties\": {\n \"Type\": {\n \"type\": \"string\"\n },\n \"Description\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"Type\",\n \"Description\"\n ],\n \"description\": \"Provide ARN of an existing Principal (role) that will be granted with read and write access to the S3 bucket (e.g. 'arn:aws:iam::123456789xxx:role/myS3RWrole'). If not specified, access will be granted to current AWS account:root only. CF deployment will fail and rollback for non-existing ARN.\"\n }\n }\n },\n \"Resources\": {\n \"properties\": {\n \"KmsKey\": {\n \"type\": \"object\",\n \"properties\": {\n \"Type\": {\n \"type\": \"string\",\n \"const\": \"AWS::KMS::Key\"\n },\n \"Properties\": {\n \"type\": \"object\"\n }\n }\n },\n \"KmsKeyAlias\": {\n \"type\": \"object\",\n \"properties\": {\n \"Type\": {\n \"type\": \"string\",\n \"const\": \"AWS::KMS::Alias\"\n },\n \"Properties\": {\n \"type\": \"object\"\n }\n }\n },\n \"Bucket\": {\n \"type\": \"object\",\n \"properties\": {\n \"Type\": {\n \"type\": \"string\",\n \"const\": \"AWS::S3::Bucket\"\n },\n \"Properties\": {\n \"type\": \"object\"\n }\n }\n },\n \"BucketPolicy\": {\n \"type\": \"object\",\n \"properties\": {\n \"Type\": {\n \"type\": \"string\",\n \"const\": \"AWS::S3::BucketPolicy\"\n },\n \"Properties\": {\n \"type\": \"object\"\n }\n }\n }\n },\n \"type\": \"object\",\n \"additionalProperties\": false\n }\n },\n \"additionalProperties\": true\n}\n", "DeprecatedStatus": "LIVE", "Visibility": "PRIVATE", "LastUpdated": "2020-11-28T05:28:40.236Z", "TimeCreated": "2020-11-28T05:28:40.236Z" }
マネコンからも確認してみましょう。
先程登録したモジュールの内容が表示されています。これでモジュールを利用する準備が整いました。
モジュールを利用するスタックの作成
以下のテンプレートを用意し、先程作成したモジュールを利用して新しくスタックを作成します。リソースFirehoseDestination
のTypeに先程作成したモジュールの名前Classmethod::S3::Bucket::MODULE
を指定するのがポイントです。
AWSTemplateFormatVersion: '2010-09-09' Description: "Create a Firehose stream that writes to S3" Resources: FirehoseDestination: Type: <先程作成したモジュール名> Properties: KMSKeyAlias: !Sub "${AWS::StackName}" ReadWriteArn: !GetAtt FirehoseRole.Arn ReadOnlyArn: !Sub 'arn:aws:iam::${AWS::AccountId}:root' FirehoseRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Sid: AssumeRole1 Effect: Allow Principal: Service: firehose.amazonaws.com Action: 'sts:AssumeRole' FirehosePolicy: Type: 'AWS::IAM::Policy' Properties: PolicyName: "ReadWrite" PolicyDocument: Version: '2012-10-17' Statement: - Sid: "KmsEncryptionDecryption" Effect: Allow Action: - 'kms:Decrypt' - 'kms:GenerateDataKey' Resource: !GetAtt FirehoseDestinationKmsKey.Arn Condition: StringEquals: kms:ViaService: !Sub 's3:${AWS::Region}.amazonaws.com' StringLike: kms:EncryptionContext:aws:s3:arn: !Sub '${FirehoseDestinationBucket.Arn}/*' - Sid: FirehoseAccess Effect: Allow Action: - kinesis:DescribeStream - kinesis:GetShardIterator - kinesis:GetRecords - kinesis:ListShards Resource: !GetAtt Firehose.Arn - Sid: "S3ListBucket" Effect: Allow Action: - 's3:ListBucket' - 's3:ListBucketByTags' - 's3:ListBucketMultipartUploads' - 's3:GetBucketLocation' Resource: !GetAtt FirehoseDestinationBucket.Arn - Sid: "S3GetPutDeleteObject" Effect: Allow Action: - 's3:DeleteObject' - 's3:DeleteObjectTagging' - 's3:GetObject' - 's3:GetObjectTagging' - 's3:PutObject' - 's3:PutObjectTagging' Resource: !Sub '${FirehoseDestinationBucket.Arn}/*' Roles: - !Ref FirehoseRole Firehose: Type: AWS::KinesisFirehose::DeliveryStream Properties: DeliveryStreamName: !Sub "${AWS::StackName}" DeliveryStreamType: DirectPut S3DestinationConfiguration: BucketARN: !GetAtt FirehoseDestinationBucket.Arn RoleARN: !GetAtt FirehoseRole.Arn EncryptionConfiguration: KMSEncryptionConfig: AWSKMSKeyARN: !GetAtt FirehoseDestinationKmsKey.Arn
このテンプレートからスタックを作成してみます。
スタックの作成完了後に、作成されたリソースを確認するとモジュール側で定義したS3バケット等のリソースが作成されていることが分かります。
また、「テンプレートタブ」から「処理されたテンプレートの表示」を選択すると以下のように表示されました。
Description: Create a Firehose stream that writes to S3 Parameters: { } Mappings: { } Conditions: { } Rules: { } Resources: ...略 FirehoseDestinationKmsKeyAlias: Type: AWS::KMS::Alias Metadata: AWS::Cloudformation::Module: TypeHierarchy: Classmethod::S3::Bucket::MODULE LogicalIdHierarchy: FirehoseDestination Properties: TargetKeyId: Ref: FirehoseDestinationKmsKey AliasName: Fn::Sub: - alias/${KMSKeyAlias} - KMSKeyAlias: Fn::Sub: ${AWS::StackName} FirehoseDestinationBucketPolicy: Type: AWS::S3::BucketPolicy Metadata: AWS::Cloudformation::Module: TypeHierarchy: Classmethod::S3::Bucket::MODULE LogicalIdHierarchy: FirehoseDestination Properties: Bucket: Ref: FirehoseDestinationBucket PolicyDocument: Version: '2012-10-17' Statement: - Condition: StringEquals: s3:x-amz-server-side-encryption: AES256 Action: s3:PutObject Resource: Fn::Sub: arn:${AWS::Partition}:s3:::${FirehoseDestinationBucket}/* Effect: Deny Principal: '*' Sid: DenyIncorrectEncryptionHeader - Condition: StringEquals: s3:x-amz-acl: - public-read - public-read-write - authenticated-read Action: - s3:PutObject - s3:PutObjectAcl Resource: Fn::Sub: arn:${AWS::Partition}:s3:::${FirehoseDestinationBucket}/* Effect: Deny Principal: '*' Sid: DenyPublicReadACL - Condition: StringLike: s3:x-amz-grant-read: - '*http://acs.amazonaws.com/groups/global/AllUsers*' - '*http://acs.amazonaws.com/groups/global/AuthenticatedUsers*' Action: - s3:PutObject - s3:PutObjectAcl Resource: Fn::Sub: arn:${AWS::Partition}:s3:::${FirehoseDestinationBucket}/* Effect: Deny Principal: '*' Sid: DenyPublicReadGrant - Condition: Bool: aws:SecureTransport: false Action: - s3:PutObject - s3:GetObject Resource: Fn::Sub: arn:${AWS::Partition}:s3:::${FirehoseDestinationBucket}/* Effect: Deny Principal: '*' Sid: DenyNonHttpsConnections - Action: - s3:GetObject - s3:GetObjectTagging - s3:ListBucket - s3:ListBucketByTags Resource: - Fn::Sub: arn:${AWS::Partition}:s3:::${FirehoseDestinationBucket} - Fn::Sub: arn:${AWS::Partition}:s3:::${FirehoseDestinationBucket}/* Effect: Allow Principal: AWS: Fn::Sub: arn:aws:iam::${AWS::AccountId}:root Sid: Give ReadOnlyRole access to get objects from bucket and list bucket - Action: - s3:DeleteObject - s3:DeleteObjectTagging - s3:GetObject - s3:GetObjectTagging - s3:ListBucket - s3:ListBucketByTags - s3:PutObject - s3:PutObjectTagging Resource: - Fn::Sub: arn:${AWS::Partition}:s3:::${FirehoseDestinationBucket} - Fn::Sub: arn:${AWS::Partition}:s3:::${FirehoseDestinationBucket}/* Effect: Allow Principal: AWS: Fn::GetAtt: FirehoseRole.Arn Sid: Give the ReadWriteRole access to get and put objects from and to bucket and list bucket and multipart uploads FirehoseDestinationBucket: Type: AWS::S3::Bucket Metadata: AWS::Cloudformation::Module: TypeHierarchy: Classmethod::S3::Bucket::MODULE LogicalIdHierarchy: FirehoseDestination Properties: PublicAccessBlockConfiguration: RestrictPublicBuckets: true BlockPublicPolicy: true BlockPublicAcls: true IgnorePublicAcls: true BucketName: Fn::Sub: ${AWS::StackName}-${AWS::AccountId}-${AWS::Region} BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: aws:kms KMSMasterKeyID: Ref: FirehoseDestinationKmsKey AccessControl: BucketOwnerFullControl ...略 Outputs: FirehoseDestinationBucketArn: Description: ARN of the bucket created. Value: Fn::GetAtt: - FirehoseDestinationBucket - Arn FirehoseDestinationBucketName: Description: Name of the bucket created. Value: Ref: FirehoseDestinationBucket FirehoseDestinationKmsKeyAlias: Description: Alias of SSE-KMS Customer Managed Key used to encrypt S3 bucket content. Value: Ref: FirehoseDestinationKmsKeyAlias FirehoseDestinationKmsKeyArn: Description: ARN of SSE-KMS Customer Managed Key used to encrypt S3 bucket content. Value: Fn::GetAtt: - FirehoseDestinationKmsKey - Arn AWSTemplateFormatVersion: '2010-09-09' Hooks: { }
モジュールで指定したリソースの定義がテンプレートに取り込まれて展開されていることが分かります。これで別のスタックを作成する時も同じS3バケットやKMSの定義を共通利用することができそうですね。
また、スタックの「出力」を確認すると、モジュール側のOutputsセクションで定義した値が出力されていることが分かります。
このようにモジュールを利用するテンプレートとモジュールの間でパラメータを受け渡ししたり、Fn::RefやFn::Subで参照することも可能になっています。これは便利に使えそうですね。
まとめ
CloudFormationのモジュール機能についてご紹介しました。テンプレートの再利用が捗りそうな良いアップデートですね。これまでAWS::Includeやマクロを使っていたユースケースではモジュールへの置き換えを検討しても良いかもしれません。既にGitHubでモジュールのサンプルがいくつか公開されているので、このあたりを参考にモジュールの作成に挑戦してみるのも面白そうです。
あとはモジュール用のテンプレートがYAMLに対応してくれれば...期待して待ちましょう。